package coupon

import (
	"context"
	// "fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	enum "mall/service/backend/common/enum/promotion"
	"mall/service/promotion/model"
	"mall/service/promotion/rpc/internal/svc"
	"mall/service/promotion/rpc/types/promotion"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetCouponTemlateListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetCouponTemlateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCouponTemlateListLogic {
	return &GetCouponTemlateListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetCouponTemlateListLogic) GetCouponTemlateList(in *promotion.BaseListReq) (*promotion.CouponTemlateListResp, error) {
	if in.Filter == nil || len(*in.Filter) == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList preloadMap: %+v", preloadMap))
	// map[couponTemplate:[Id Name Desc Members Type Mutex Scope Mode Threshhold DiscountMode DiscountValue PerLimit ExpireMode ExpireValue StartedAt EndedAt] couponTotal:[] promotionMedia:[Uid Url Thumbnail Order altText] promotionMeta:[mediaUrl]]
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList eagerLoadMap: %+v", eagerLoadMap))
	// map[couponTemplate:[couponTotal promotionMeta promotionMedia]]

	couponTemplateEntityName := model.RpcEntityNames.PromotionCouponTemplate
	if _, ok := model.RpcEntityPreloadMap[couponTemplateEntityName]; !ok {
		return &promotion.CouponTemlateListResp{}, nil
	}

	couponTemplateRepositoryName := model.RepositoryNames.PromotionCouponTemplate
	columnMap, ok := model.RepositoryPreloadMap[couponTemplateRepositoryName]
	if !ok {
		return &promotion.CouponTemlateListResp{}, nil
	}

	var selectCols []string
	if preloadFields, ok := preloadMap[couponTemplateEntityName]; ok {
		selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
	}

	filterMap := modelx.ParseFilter(in.Filter, columnMap)
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList filterMap: %+v", filterMap))
	// map[coupon_type:map[in:[1 2]] status:map[equalTo:[10]]]
	if filterMap == nil {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	orderBy := modelx.ParseSorter(in.Sorter, columnMap)
	// logrus.Info(fmt.Sprintf("GetCouponTemlateList orderBy: %s", orderBy))

	var keyword string
	if in.Search != nil && len(strings.TrimSpace(*in.Search)) > 0 {
		keyword = strings.TrimSpace(*in.Search)
	}

	res, count, err := l.svcCtx.CouponTemplate.GetList(l.ctx, model.M{
		"offset":  in.Offset,
		"limit":   in.Limit,
		"search":  keyword,
		"orderBy": orderBy,
		"select":  strings.Join(selectCols, ","),
	}, 2, filterMap)
	if err != nil {
		return nil, err
	}

	hasNextPage, hasPreviousPage := modelx.ParseCount(count)
	resp := &promotion.CouponTemlateListResp{
		HasNextPage:     &hasNextPage,
		HasPreviousPage: &hasPreviousPage,
	}

	if len(res) > 0 {
		var ids []any
		for _, v := range res {
			ids = append(ids, v.ID)
		}

		couponTotalMap := make(map[uint64]uint64)
		subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadEntities, ok := eagerLoadMap[couponTemplateEntityName]; ok {
			for _, entity := range eagerLoadEntities {
				if entity == "couponTotal" {
					if in.VisitorId != nil && *in.VisitorId > 0 {
						couponFilterMap := make(map[string]map[string][]any)
						couponFilterMap[model.PromotionCouponColumns.TemplateID] = map[string][]any{
							"in": ids,
						}
						couponFilterMap[model.PromotionCouponColumns.UserID] = map[string][]any{
							"equalTo": {*in.VisitorId},
						}
						list, _, err := l.svcCtx.Coupon.GetList(l.ctx, model.M{}, 5, couponFilterMap)
						if err != nil {
							return nil, err
						}

						if len(list) > 0 {
							for _, v := range list {
								couponTotalMap[v.TemplateID] = v.OrderID
							}
						}
					}
					continue
				}

				if entitySettingMap, ok := model.RpcEntityPreloadMap[entity]; ok {
					subEntitySettingMap[entity] = entitySettingMap
				}
			}
		}

		idMetaMap := make(map[uint64][]*promotion.MetaInfo)
		promotionMetaEntityName := model.RpcEntityNames.PromotionCouponMeta
		if entitySettingMap, ok := subEntitySettingMap[promotionMetaEntityName]; ok {
			if subPreloadFields, ok := preloadMap[promotionMetaEntityName]; ok {
				var metaKeys []any
				metaKeyMap := make(map[string]string)
				for _, field := range subPreloadFields {
					if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
						metaKeys = append(metaKeys, fieldSetting.Name)
						metaKeyMap[fieldSetting.Name] = field
					}
				}
				if len(metaKeys) > 0 {
					subFilterMap := make(map[string]map[string][]any)
					subFilterMap[model.PromotionCouponMetumColumns.CouponTemplateID] = map[string][]any{
						"in": ids,
					}
					subFilterMap[model.PromotionCouponMetumColumns.MetaKey] = map[string][]any{
						"in": metaKeys,
					}
					var orderBy string = model.PromotionCouponMetumColumns.MetaID + " DESC"
					list, _, err := l.svcCtx.CouponTemplate.GetMeta(l.ctx, model.M{
						"orderBy": orderBy,
					}, -1, subFilterMap)
					if err != nil && err != model.ErrNotFound {
						return nil, err
					}

					if len(list) > 0 {
						keyMap := make(map[uint64]map[string]struct{})
						for _, v := range list {
							key := v.MetaKey
							if len(key) > 0 && v.MetaValue.Valid {
								if subMap, ok := keyMap[v.MetaID]; ok { // 防重复
									if _, ok := subMap[key]; ok {
										continue
									}

									keyMap[v.MetaID][v.MetaKey] = struct{}{}
								} else {
									keyMap[v.MetaID] = make(map[string]struct{})
								}

								if field, ok := metaKeyMap[key]; ok {
									idMetaMap[v.CouponTemplateID] = append(idMetaMap[v.CouponTemplateID], &promotion.MetaInfo{
										Id:    &v.MetaID,
										Key:   pointy.GetPointer(field),
										Value: pointy.GetPointer(v.MetaValue.String),
									})
								}
							}
						}
					}
				}
			}
		}

		idMediaMap := make(map[uint64][]*promotion.MediaInfo)
		promotionMediaEntityName := model.RpcEntityNames.PromotionMedia
		if _, ok := subEntitySettingMap[promotionMediaEntityName]; ok {
			promotionMediaRepositoryName := model.RepositoryNames.PromotionMedia
			subPreloadFields, hasSubPreloadFields := preloadMap[promotionMediaEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[promotionMediaRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.PromotionAttachmentRelationshipColumns.ObjectID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.PromotionAttachmentRelationshipColumns.ObjectType] = map[string][]any{
					"equalTo": {globalkey.AttachmentLogTypePromotionCoupon},
				}
				subSelectCols = append(subSelectCols, model.PromotionAttachmentRelationshipColumns.ObjectID)
				orderBy := model.PromotionAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
					model.PromotionAttachmentRelationshipColumns.AttachmentOrder
				list, _, err := l.svcCtx.CouponTemplate.GetMedia(l.ctx, model.M{
					"orderBy": orderBy,
					"select":  strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &promotion.MediaInfo{
							Uid:       &v.AttachmentRelationshipID,
							Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
							Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
							Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
							Uri:       &v.AttachmentSource,
							Url:       &v.AttachmentImageURL,
							AltText:   &v.AttachmentImageAlt,
							MetaData:  &v.AttachmentImageMeta,
						})
					}
				}
			}
		}

		idCouponMap := make(map[uint64]*promotion.CouponInfo)
		couponEntityName := model.RpcEntityNames.PromotionCoupon
		if _, ok := subEntitySettingMap[couponEntityName]; ok {
			couponRepositoryName := model.RepositoryNames.PromotionCoupon
			subPreloadFields, hasSubPreloadFields := preloadMap[couponEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[couponRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

				couponFilterMap := make(map[string]map[string][]any)
				couponFilterMap[model.PromotionCouponColumns.TemplateID] = map[string][]any{
					"in": ids,
				}
				couponFilterMap[model.PromotionCouponColumns.Status] = map[string][]any{
					"equalTo": {globalkey.StatusStarted},
				}

				if in.VisitorId != nil && *in.VisitorId > 0 {
					couponFilterMap[model.PromotionCouponColumns.UserID] = map[string][]any{
						"equalTo": {*in.VisitorId},
					}
				}
				list, _, err := l.svcCtx.Coupon.GetList(l.ctx, model.M{
					"oldest": "1",
					"select": strings.Join(subSelectCols, ","),
				}, -1, couponFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						if _, ok := idCouponMap[v.TemplateID]; !ok {
							idCouponMap[v.TemplateID] = &promotion.CouponInfo{
								Id:         &v.CouponID,
								ActivityId: &v.ActivityID,
								TemplateId: &v.TemplateID,
								Code:       &v.CouponCode,
								UserId:     &v.UserID,
								OrderId:    &v.OrderID,
								StartedAt:  pointy.GetPointer(modelx.FormatNullDotTime(v.StartedAt, "2006-01-02 15:04")),
								EndedAt:    pointy.GetPointer(modelx.FormatNullDotTime(v.EndedAt, "2006-01-02 15:04")),
								UsedAt:     pointy.GetPointer(modelx.FormatNullDotTime(v.UsedAt, "2006-01-02 15:04")),
								Status:     pointy.GetPointer(uint32(v.Status)),
							}
						}
					}
				}
			}
		}

		for _, v := range res {
			var threshold string
			switch v.Mode {
			case enum.PromotionCouponModeAmount:
				threshold = moneyx.FormatAmount(int64(v.Threshhold))
			case enum.PromotionCouponModeNumber:
				threshold = strconv.FormatUint(v.Threshhold, 10)
			}
			var discountValue string
			var regions *string
			switch v.DiscountMode {
			case enum.PromotionCouponRuleAmount:
				discountValue = moneyx.FormatAmount(int64(v.DiscountValue))
			case enum.PromotionCouponRuleDiscount:
				discountValue = strconv.FormatUint(v.DiscountValue, 10)
			case enum.PromotionCouponRuleDeduction:
				discountValue = strconv.FormatUint(v.DiscountValue, 10)
			case enum.PromotionCouponRuleOrderFreeShipping, enum.PromotionCouponRuleProductFreeShipping:
				if v.Regions.Valid {
					regions = pointy.GetPointer(string(v.Regions.JSON))
				}
			case enum.PromotionCouponRuleFixedPrice:
				discountValue = moneyx.FormatAmount(int64(v.DiscountValue))
			}
			var couponTotal uint32
			if cnt, ok := couponTotalMap[v.ID]; ok {
				couponTotal = uint32(cnt)
			}
			var couponInfo *promotion.CouponInfo
			if coupon, ok := idCouponMap[v.ID]; ok {
				couponInfo = coupon
			}
			var media []*promotion.MediaInfo
			if ms, ok := idMediaMap[v.ID]; ok {
				media = ms[:]
			}
			var meta []*promotion.MetaInfo
			if ms, ok := idMetaMap[v.ID]; ok {
				meta = ms[:]
			}

			var gemPrice *string
			if v.GemPrice > 0 {
				gemPrice = pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice)))
			}

			resp.List = append(resp.List, &promotion.CouponTemlateInfo{
				Id:                &v.ID,
				Name:              &v.Name,
				Desc:              &v.Description,
				Condition:         pointy.GetPointer(uint32(v.Mode)),
				ConditionValue:    pointy.GetPointer(threshold),
				DiscountMode:      pointy.GetPointer(uint32(v.DiscountMode)),
				DiscountValue:     pointy.GetPointer(discountValue),
				ScopeType:         pointy.GetPointer(uint32(v.ScopeType)),
				GemPrice:          gemPrice,
				ExpireMode:        pointy.GetPointer(uint32(v.ExpireMode)),
				ExpireValue:       pointy.GetPointer(uint32(v.ExpireValue)),
				StartedAt:         pointy.GetPointer(modelx.FormatNullDotTime(v.StartedAt, "")),
				EndedAt:           pointy.GetPointer(modelx.FormatNullDotTime(v.EndedAt, "")),
				Sort:              pointy.GetPointer(uint32(v.Sort)),
				Status:            pointy.GetPointer(uint32(v.Status)),
				MemberRestriction: &v.MemberRestriction,
				CouponType:        pointy.GetPointer(uint32(v.CouponType)),
				MutexType:         pointy.GetPointer(uint32(v.MutexType)),
				Count:             pointy.GetPointer(uint32(v.Count)),
				PerLimit:          pointy.GetPointer(uint32(v.PerLimit)),
				CouponTotal:       pointy.GetPointer(couponTotal),
				CreatedAt:         pointy.GetPointer(modelx.FormatTime(v.CreatedAt, "")),
				UpdatedAt:         pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
				Regions:           regions,
				CouponInfo:        couponInfo,
				Media:             media,
				Meta:              meta,
			})
		}
	}

	resp.Total = uint64(len(resp.List))
	return resp, nil
}
